﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace MouldingAndMillwork.SSIS
{
    public partial class SSISUtility
    {
        #region CLASS: ConnectionPool
        public class ConnectionPool
        {
            public delegate IDbConnection IDbConnectionCreator(object transaction);

            #region Private Variables
            private readonly object _lock = new object();
            private IDbConnectionCreator _creator;
            private bool _acquired;
            private IDbConnection[] _connections;
            private bool[] _connectionsInUse;
            #endregion

            #region Constructor
            public ConnectionPool(int size, IDbConnectionCreator connectionCreator)
            {
                this._creator = connectionCreator;
                this._connections = new IDbConnection[size];
                this._connectionsInUse = new bool[size];
                this._acquired = false;
                for (int connectionNumber = 0; connectionNumber < size; connectionNumber++)
                {
                    this._connections[connectionNumber] = null;
                    this._connectionsInUse[connectionNumber] = false;
                }
            }
            #endregion

            #region Properties
            public bool HasPoolBeenAcquired
            {
                get { return this._acquired; }
            }
            #endregion

            #region Pool Methods
            public void AcquirePool(object transaction)
            {
                lock (this._lock)
                {
                    for (int connNum = 0; connNum < this._connections.Length; connNum++)
                    {
                        this._connections[connNum] = this._creator(transaction);
                    }
                    this._acquired = true;
                }
            }

            public void ReleasePool()
            {
                lock (this._lock)
                {
                    if (this._acquired)
                    {
                        for (int connNum = 0; connNum < this._connections.Length; connNum++)
                        {
                            if ((this._connections[connNum] != null) && (this._connections[connNum].State == ConnectionState.Open))
                            {
                                this._connections[connNum].Close();
                                this._connections[connNum] = null;
                            }
                        }
                        this._acquired = false;
                    }
                }
            }
            #endregion

            #region Connection Use
            public PooledConnection UseAConnection()
            {
                PooledConnection connection = null;

                while (connection == null)
                {
                    lock (this._lock)
                    {
                        for (int connectionNumber = 0; connectionNumber < this._connectionsInUse.Length; connectionNumber++)
                        {
                            if (!this._connectionsInUse[connectionNumber])
                            {
                                connection = new PooledConnection(connectionNumber, this, this._connections[connectionNumber]);
                                this._connectionsInUse[connectionNumber] = true;
                                break;
                            }
                        }
                    }

                    if (connection == null)
                    {
                        System.Threading.Thread.Sleep(50);
                    }
                }

                return connection;
            }

            public void StopUsingAConnection(int connectionNumber)
            {
                lock (this._lock)
                {
                    this._connectionsInUse[connectionNumber] = false;
                }
            }
            #endregion

            #region CLASS: PooledConnection
            public class PooledConnection : IDisposable
            {
                #region Private Variables
                private int _myConnectionNumber;
                private ConnectionPool _parentPool;
                private IDbConnection _connection;
                #endregion

                #region Constructor
                public PooledConnection(int connectionNumber, ConnectionPool parentPool, IDbConnection connection)
                {
                    this._myConnectionNumber = connectionNumber;
                    this._parentPool = parentPool;
                    this._connection = connection;
                }
                #endregion

                #region Properties
                public IDbConnection Connection
                {
                    get { return this._connection; }
                }
                #endregion

                #region IDisposable Members
                public void Dispose()
                {
                    this._parentPool.StopUsingAConnection(this._myConnectionNumber);
                }
                #endregion
            }
            #endregion
        }
        #endregion
    }
}
